﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using UCS.XNA.Utils;

namespace UCS.XNA.CombatBiplanes
{
    public class CollisionDetector
    {
        #region Attributes and Properties

        private List<Airplane> _airplanes;
        public List<Airplane> Airplanes
        {
            get { return this._airplanes; }
            set { _airplanes = value; }
        }

        private RefreshableCollection<Rocket> _rockets;
        public RefreshableCollection<Rocket> Rockets
        {
            get { return this._rockets; }
            set { this._rockets = value; }
        }

        private Rectangle _screenBounds;
        public Rectangle ScreenBounds
        {
            get { return this._screenBounds; }
            set { this._screenBounds = value; }
        }

        #endregion

        #region Events

        public event EventHandler<AirplaneCollidedEventArgs> AirplaneCollided;
        public event EventHandler<AirplaneRocketCollidedEventArgs> AirplaneRocketCollided;
        public event EventHandler<RocketEventArgs> RocketScreenBoundsCollided;

        #endregion

        #region Constructors

        public CollisionDetector(List<Airplane> airplanes, RefreshableCollection<Rocket> rockets, Rectangle screenBounds)
        {
            this._airplanes = airplanes;
            this._rockets = rockets;
            this._screenBounds = screenBounds;
        }

        #endregion

        #region Public Methods

        public void Update(GameTime gameTime)
        {
            this._rockets.Refresh();

            this.TestAirplanesCollisions();
            this.TestAirplanesRocketsCollisions();
            this.TestRocketsScreenBoundsCollisions();
        }

        #endregion

        #region Private Methods

        private void TestAirplanesCollisions()
        {
            for (int i = 0; i < this._airplanes.Count; i++)
            {
                Airplane airplane = this._airplanes[i];
                for (int j = 0; j < i + 1; j++)
                {
                    Airplane comparingAirplane = this._airplanes[j];

                    if (airplane != comparingAirplane)
                    {
                        if (this.IsDiameterColliding(airplane.Bounds, comparingAirplane.Bounds))
                        {
                            Vector2 collision = this.TexturesCollide(airplane.ColorData, airplane.GetMatrix(), comparingAirplane.ColorData, comparingAirplane.GetMatrix());
                            if (collision.X != -1 && collision.Y != -1 && this.AirplaneCollided != null)
                                this.AirplaneCollided(this, new AirplaneCollidedEventArgs(airplane, comparingAirplane));
                        }
                    }
                }
            }
        }

        private void TestAirplanesRocketsCollisions()
        {
            foreach (var rocket in this._rockets)
            {
                foreach (var airplane in this._airplanes)
                {
                    if (airplane != rocket.Source)
                    {
                        //if (this.IsDiameterColliding(rocket.Bounds, airplane.Bounds))
                        //{
                            Vector2 collision = this.TexturesCollide(rocket.ColorData, rocket.GetMatrix(), airplane.ColorData, airplane.GetMatrix());
                            if (collision.X != -1 && collision.Y != -1 && this.AirplaneRocketCollided != null)
                                this.AirplaneRocketCollided(this, new AirplaneRocketCollidedEventArgs(airplane, rocket));
                        //}
                    }
                }
            }
        }

        private void TestRocketsScreenBoundsCollisions()
        {
            foreach (Rocket rocket in this._rockets)
            {
                if (rocket.Position.X < 0 || rocket.Position.X > this._screenBounds.Right ||
                    rocket.Position.Y < 0 || rocket.Position.Y > this._screenBounds.Bottom)
                {
                    if (this.RocketScreenBoundsCollided != null)
                        RocketScreenBoundsCollided(this, new RocketEventArgs(rocket));
                }
            }
        }

        private Vector2 TexturesCollide(Color[,] colors1, Matrix matrix1, Color[,] colors2, Matrix matrix2)
        {
            Matrix matrix1to2 = matrix1 * Matrix.Invert(matrix2);

            for (int x = 0; x < colors1.GetLength(0); x++)
            {
                for (int y = 0; y < colors1.GetLength(1); y++)
                {
                    Vector2 position1 = new Vector2(x, y);
                    Vector2 position2 = Vector2.Transform(position1, matrix1to2);
                    
                    if (position2.X >= 0 && position2.X < colors2.GetLength(0) &&
                        position2.Y >= 0 && position2.Y < colors2.GetLength(1))
                    {
                        if (colors1[x, y].A > 0 && colors2[(int)position2.X, (int)position2.Y].A > 0)
                        {
                            Vector2 screenPosition = Vector2.Transform(position1, matrix1);
                            return screenPosition;
                        }
                    }
                }
            }

            return new Vector2(-1, -1);
        }

        private bool IsDiameterColliding(Rectangle bounds1, Rectangle bounds2)
        {
            float higherSize1Radius, higherSize2Radius;
            
            higherSize1Radius = Math.Max(bounds1.Width, bounds1.Height) / 2f;
            higherSize2Radius = Math.Max(bounds2.Width, bounds2.Height) / 2f;

            return (Vector2.Distance(new Vector2(bounds1.Center.X, bounds1.Center.Y), new Vector2(bounds2.Center.X, bounds2.Center.Y)) <= higherSize1Radius + higherSize2Radius);
        }

        #endregion
    }

    public class AirplaneCollidedEventArgs : EventArgs
    {
        #region Attributes and Properties

        private Airplane _airplane1;
        public Airplane Airplane1
        {
          get { return this._airplane1; }
        }

        private Airplane _airplane2;
        public Airplane Airplane2
        {
          get { return this._airplane2; }
        }

        #endregion

        #region Constructors

        public AirplaneCollidedEventArgs(Airplane airplane1, Airplane airplane2)
        {
            this._airplane1 = airplane1;
            this._airplane2 = airplane2;
        }

        #endregion
    }

    public class AirplaneRocketCollidedEventArgs : EventArgs
    {
        #region Attributes and Properties

        private Airplane _airplane;
        public Airplane Airplane
        {
            get { return this._airplane; }
        }

        private Rocket _rocket;
        public Rocket Rocket
        {
            get { return this._rocket; }
        }

        #endregion

        #region Constructors

        public AirplaneRocketCollidedEventArgs(Airplane airplane, Rocket rocket)
        {
            this._airplane = airplane;
            this._rocket = rocket;
        }

        #endregion
    }
}
